#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <common_header.h>
#include <cfloat>

namespace d3dx
{
	struct swapchain_desc : DXGI_SWAP_CHAIN_DESC1
	{
		swapchain_desc() = default;
		constexpr void init_unvsync(u32 width, u32 height, u32 back_buffer_count)
		{
			this->Width = width;
			this->Height = height;
			this->Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			this->Stereo = FALSE;
			this->SampleDesc.Count = 1u;
			this->SampleDesc.Quality = 0u;
			this->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
			this->BufferCount = back_buffer_count;
			this->Scaling = DXGI_SCALING_STRETCH;
			this->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
			this->AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
			this->Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
		}
	};

	constexpr struct
	{
		const D3D12_COMMAND_QUEUE_DESC direct{
			D3D12_COMMAND_LIST_TYPE_DIRECT,        //D3D12_COMMAND_LIST_TYPE      Type;
			D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,   //INT 						  Priority;
			D3D12_COMMAND_QUEUE_FLAG_NONE,		   //D3D12_COMMAND_QUEUE_FLAGS    Flags;
			0u,									   //UINT 						  NodeMask;
		};

		const D3D12_COMMAND_QUEUE_DESC copy{
			D3D12_COMMAND_LIST_TYPE_COPY,          //D3D12_COMMAND_LIST_TYPE      Type;
			D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,   //INT 						  Priority;
			D3D12_COMMAND_QUEUE_FLAG_NONE,		   //D3D12_COMMAND_QUEUE_FLAGS    Flags;
			0u,									   //UINT 						  NodeMask;
		};
	}queue_descriptors;

	constexpr struct
	{
		const DXGI_SWAP_CHAIN_FULLSCREEN_DESC full{
			{},                                       //DXGI_RATIONAL                RefreshRate;
			DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,  	  //DXGI_MODE_SCANLINE_ORDER 	 ScanlineOrdering;
			DXGI_MODE_SCALING_UNSPECIFIED,			  //DXGI_MODE_SCALING 			 Scaling;
			FALSE									  //BOOL 						 Windowed;
		};

		const DXGI_SWAP_CHAIN_FULLSCREEN_DESC screen{
			{},                                       //DXGI_RATIONAL                RefreshRate;
			DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,  	  //DXGI_MODE_SCANLINE_ORDER 	 ScanlineOrdering;
			DXGI_MODE_SCALING_UNSPECIFIED,			  //DXGI_MODE_SCALING 			 Scaling;
			TRUE									  //BOOL 						 Windowed;
		};
	}swap_chain_fullscreen_desc{};

	constexpr struct
	{
		const D3D12_HEAP_PROPERTIES default_heap
		{
			D3D12_HEAP_TYPE_DEFAULT,
			D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
			D3D12_MEMORY_POOL_UNKNOWN,
			0,
			0
		};
		const D3D12_HEAP_PROPERTIES upload_heap
		{
			D3D12_HEAP_TYPE_UPLOAD,
			D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
			D3D12_MEMORY_POOL_UNKNOWN,
			0,
			0
		};
	}heap_properties{};

	constexpr struct
	{
		const D3D12_BLEND_DESC Opaque =
		{
			FALSE, // AlphaToCoverageEnable
			FALSE, // IndependentBlendEnable
			{ {
				FALSE, // BlendEnable
				FALSE, // LogicOpEnable
				D3D12_BLEND_ONE, // SrcBlend
				D3D12_BLEND_ZERO, // DestBlend
				D3D12_BLEND_OP_ADD, // BlendOp
				D3D12_BLEND_ONE, // SrcBlendAlpha
				D3D12_BLEND_ZERO, // DestBlendAlpha
				D3D12_BLEND_OP_ADD, // BlendOpAlpha
				D3D12_LOGIC_OP_NOOP,
				D3D12_COLOR_WRITE_ENABLE_ALL
			} }
		};

		const D3D12_BLEND_DESC AlphaBlend =
		{
			FALSE, // AlphaToCoverageEnable
			FALSE, // IndependentBlendEnable
			{ {
				TRUE, // BlendEnable
				FALSE, // LogicOpEnable
				D3D12_BLEND_ONE, // SrcBlend
				D3D12_BLEND_INV_SRC_ALPHA, // DestBlend
				D3D12_BLEND_OP_ADD, // BlendOp
				D3D12_BLEND_ONE, // SrcBlendAlpha
				D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha
				D3D12_BLEND_OP_ADD, // BlendOpAlpha
				D3D12_LOGIC_OP_NOOP,
				D3D12_COLOR_WRITE_ENABLE_ALL
			} }
		};

		const D3D12_BLEND_DESC Additive =
		{
			FALSE, // AlphaToCoverageEnable
			FALSE, // IndependentBlendEnable
			{ {
				TRUE, // BlendEnable
				FALSE, // LogicOpEnable
				D3D12_BLEND_SRC_ALPHA, // SrcBlend
				D3D12_BLEND_ONE, // DestBlend
				D3D12_BLEND_OP_ADD, // BlendOp
				D3D12_BLEND_SRC_ALPHA, // SrcBlendAlpha
				D3D12_BLEND_ONE, // DestBlendAlpha
				D3D12_BLEND_OP_ADD, // BlendOpAlpha
				D3D12_LOGIC_OP_NOOP,
				D3D12_COLOR_WRITE_ENABLE_ALL
			} }
		};

		const D3D12_BLEND_DESC NonPremultiplied =
		{
			FALSE, // AlphaToCoverageEnable
			FALSE, // IndependentBlendEnable
			{ {
				TRUE, // BlendEnable
				FALSE, // LogicOpEnable
				D3D12_BLEND_SRC_ALPHA, // SrcBlend
				D3D12_BLEND_INV_SRC_ALPHA, // DestBlend
				D3D12_BLEND_OP_ADD, // BlendOp
				D3D12_BLEND_SRC_ALPHA, // SrcBlendAlpha
				D3D12_BLEND_INV_SRC_ALPHA, // DestBlendAlpha
				D3D12_BLEND_OP_ADD, // BlendOpAlpha
				D3D12_LOGIC_OP_NOOP,
				D3D12_COLOR_WRITE_ENABLE_ALL
			} }
		};
	}blend_descs{};

	constexpr struct
	{
		const D3D12_DEPTH_STENCIL_DESC DepthNone =
		{
			FALSE, // DepthEnable
			D3D12_DEPTH_WRITE_MASK_ZERO,
			D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc
			FALSE, // StencilEnable
			D3D12_DEFAULT_STENCIL_READ_MASK,
			D3D12_DEFAULT_STENCIL_WRITE_MASK,
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			}, // FrontFace
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			} // BackFace
		};

		const D3D12_DEPTH_STENCIL_DESC DepthDefault =
		{
			TRUE, // DepthEnable
			D3D12_DEPTH_WRITE_MASK_ALL,
			D3D12_COMPARISON_FUNC_LESS, // DepthFunc
			FALSE, // StencilEnable
			D3D12_DEFAULT_STENCIL_READ_MASK,
			D3D12_DEFAULT_STENCIL_WRITE_MASK,
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			}, // FrontFace
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			} // BackFace
		};

		const D3D12_DEPTH_STENCIL_DESC DepthRead =
		{
			TRUE, // DepthEnable
			D3D12_DEPTH_WRITE_MASK_ZERO,
			D3D12_COMPARISON_FUNC_LESS_EQUAL, // DepthFunc
			FALSE, // StencilEnable
			D3D12_DEFAULT_STENCIL_READ_MASK,
			D3D12_DEFAULT_STENCIL_WRITE_MASK,
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			}, // FrontFace
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			} // BackFace
		};

		const D3D12_DEPTH_STENCIL_DESC DepthReverseZ =
		{
			TRUE, // DepthEnable
			D3D12_DEPTH_WRITE_MASK_ALL,
			D3D12_COMPARISON_FUNC_GREATER_EQUAL, // DepthFunc
			FALSE, // StencilEnable
			D3D12_DEFAULT_STENCIL_READ_MASK,
			D3D12_DEFAULT_STENCIL_WRITE_MASK,
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			}, // FrontFace
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			} // BackFace
		};

		const D3D12_DEPTH_STENCIL_DESC DepthReadReverseZ =
		{
			TRUE, // DepthEnable
			D3D12_DEPTH_WRITE_MASK_ZERO,
			D3D12_COMPARISON_FUNC_GREATER_EQUAL, // DepthFunc
			FALSE, // StencilEnable
			D3D12_DEFAULT_STENCIL_READ_MASK,
			D3D12_DEFAULT_STENCIL_WRITE_MASK,
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			}, // FrontFace
			{
				D3D12_STENCIL_OP_KEEP, // StencilFailOp
				D3D12_STENCIL_OP_KEEP, // StencilDepthFailOp
				D3D12_STENCIL_OP_KEEP, // StencilPassOp
				D3D12_COMPARISON_FUNC_ALWAYS // StencilFunc
			} // BackFace
		};
	}depth_stencil_descs{};

	constexpr struct
	{
		const D3D12_RASTERIZER_DESC CullNone =
		{
			D3D12_FILL_MODE_SOLID,
			D3D12_CULL_MODE_NONE,
			FALSE, // FrontCounterClockwise
			D3D12_DEFAULT_DEPTH_BIAS,
			D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
			D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
			TRUE, // DepthClipEnable
			TRUE, // MultisampleEnable
			FALSE, // AntialiasedLineEnable
			0, // ForcedSampleCount
			D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
		};

		const D3D12_RASTERIZER_DESC CullClockwise =
		{
			D3D12_FILL_MODE_SOLID,
			D3D12_CULL_MODE_FRONT,
			FALSE, // FrontCounterClockwise
			D3D12_DEFAULT_DEPTH_BIAS,
			D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
			D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
			TRUE, // DepthClipEnable
			TRUE, // MultisampleEnable
			FALSE, // AntialiasedLineEnable
			0, // ForcedSampleCount
			D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
		};

		const D3D12_RASTERIZER_DESC CullCounterClockwise =
		{
			D3D12_FILL_MODE_SOLID,
			D3D12_CULL_MODE_BACK,
			FALSE, // FrontCounterClockwise
			D3D12_DEFAULT_DEPTH_BIAS,
			D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
			D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
			TRUE, // DepthClipEnable
			TRUE, // MultisampleEnable
			FALSE, // AntialiasedLineEnable
			0, // ForcedSampleCount
			D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
		};

		const D3D12_RASTERIZER_DESC Wireframe =
		{
			D3D12_FILL_MODE_WIREFRAME,
			D3D12_CULL_MODE_NONE,
			FALSE, // FrontCounterClockwise
			D3D12_DEFAULT_DEPTH_BIAS,
			D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
			D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
			TRUE, // DepthClipEnable
			TRUE, // MultisampleEnable
			FALSE, // AntialiasedLineEnable
			0, // ForcedSampleCount
			D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
		};
	}rasterizer_descs{};

	constexpr struct
	{
		const DXGI_FORMAT render_target{ DXGI_FORMAT_R8G8B8A8_UNORM };
		const DXGI_FORMAT depth_stencil{ DXGI_FORMAT_D24_UNORM_S8_UINT };
		const DXGI_SAMPLE_DESC sampler{ 1u,0u };
	}default_formats{};


	namespace sampler
	{
		constexpr D3D12_STATIC_SAMPLER_DESC StaticPointWrap
		(
			unsigned int shaderRegister,
			D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
			unsigned int registerSpace = 0u
		) noexcept
		{
			const D3D12_STATIC_SAMPLER_DESC s_desc = {
				D3D12_FILTER_MIN_MAG_MIP_POINT,
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW
				0, // MipLODBias
				D3D12_MAX_MAXANISOTROPY,
				D3D12_COMPARISON_FUNC_NEVER,
				D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
				0, // MinLOD
				FLT_MAX, // MaxLOD
				0, // ShaderRegister
				0, // RegisterSpace
				D3D12_SHADER_VISIBILITY_ALL,
			};
			D3D12_STATIC_SAMPLER_DESC desc = s_desc;
			desc.ShaderRegister = shaderRegister;
			desc.ShaderVisibility = shaderVisibility;
			desc.RegisterSpace = registerSpace;
			return desc;
		}

		constexpr D3D12_STATIC_SAMPLER_DESC StaticPointClamp
		(
			unsigned int shaderRegister,
			D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
			unsigned int registerSpace = 0u
		) noexcept
		{
			const D3D12_STATIC_SAMPLER_DESC s_desc = {
				D3D12_FILTER_MIN_MAG_MIP_POINT,
				D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU
				D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV
				D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW
				0, // MipLODBias
				D3D12_MAX_MAXANISOTROPY,
				D3D12_COMPARISON_FUNC_NEVER,
				D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
				0, // MinLOD
				FLT_MAX, // MaxLOD
				0, // ShaderRegister
				0, // RegisterSpace
				D3D12_SHADER_VISIBILITY_ALL,
			};
			D3D12_STATIC_SAMPLER_DESC desc = s_desc;
			desc.ShaderRegister = shaderRegister;
			desc.ShaderVisibility = shaderVisibility;
			desc.RegisterSpace = registerSpace;
			return desc;
		};

		constexpr D3D12_STATIC_SAMPLER_DESC StaticLinearWrap(
			unsigned int shaderRegister,
			D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
			unsigned int registerSpace = 0u) noexcept
		{
			const D3D12_STATIC_SAMPLER_DESC s_desc = {
				D3D12_FILTER_MIN_MAG_MIP_LINEAR,
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW
				0, // MipLODBias
				D3D12_MAX_MAXANISOTROPY,
				D3D12_COMPARISON_FUNC_NEVER,
				D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
				0, // MinLOD
				FLT_MAX, // MaxLOD
				0, // ShaderRegister
				0, // RegisterSpace
				D3D12_SHADER_VISIBILITY_ALL,
			};
			D3D12_STATIC_SAMPLER_DESC desc = s_desc;
			desc.ShaderRegister = shaderRegister;
			desc.ShaderVisibility = shaderVisibility;
			desc.RegisterSpace = registerSpace;
			return desc;
		};

		constexpr D3D12_STATIC_SAMPLER_DESC StaticAnisotropicWrap(
			unsigned int shaderRegister,
			D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
			unsigned int registerSpace = 0u) noexcept
		{
			const D3D12_STATIC_SAMPLER_DESC s_desc = {
				D3D12_FILTER_ANISOTROPIC,
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressU
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressV
				D3D12_TEXTURE_ADDRESS_MODE_WRAP, // AddressW
				0, // MipLODBias
				D3D12_MAX_MAXANISOTROPY,
				D3D12_COMPARISON_FUNC_NEVER,
				D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
				0, // MinLOD
				FLT_MAX, // MaxLOD
				0, // ShaderRegister
				0, // RegisterSpace
				D3D12_SHADER_VISIBILITY_ALL,
			};
			D3D12_STATIC_SAMPLER_DESC desc = s_desc;
			desc.ShaderRegister = shaderRegister;
			desc.ShaderVisibility = shaderVisibility;
			desc.RegisterSpace = registerSpace;
			return desc;
		};

		constexpr D3D12_STATIC_SAMPLER_DESC StaticAnisotropicClamp(
			unsigned int shaderRegister,
			D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
			unsigned int registerSpace = 0u) noexcept
		{
			const D3D12_STATIC_SAMPLER_DESC s_desc = {
				D3D12_FILTER_ANISOTROPIC,
				D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressU
				D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressV
				D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // AddressW
				0, // MipLODBias
				D3D12_MAX_MAXANISOTROPY,
				D3D12_COMPARISON_FUNC_NEVER,
				D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
				0, // MinLOD
				FLT_MAX, // MaxLOD
				0, // ShaderRegister
				0, // RegisterSpace
				D3D12_SHADER_VISIBILITY_ALL,
			};
			D3D12_STATIC_SAMPLER_DESC desc = s_desc;
			desc.ShaderRegister = shaderRegister;
			desc.ShaderVisibility = shaderVisibility;
			desc.RegisterSpace = registerSpace;
			return desc;
		};
	}

	struct resource_barrier
	{
		constexpr static u32 max_barrier_count{ 32u };
		constexpr void add(ID3D12Resource* resource,
			D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after,
			D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
			u32 subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)
		{
			auto& barrier{ _barriers[_offset] };
			barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
			barrier.Flags = flags;
			barrier.Transition.pResource = resource;
			barrier.Transition.StateBefore = before;
			barrier.Transition.StateAfter = after;
			barrier.Transition.Subresource = subresource;

			++_offset;
		}

		constexpr void add(ID3D12Resource* resource,
			D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
		{
			auto& barrier{ _barriers[_offset] };
			barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
			barrier.Flags = flags;
			barrier.UAV.pResource = resource;
			++_offset;
		}

		constexpr void add(ID3D12Resource* resource_before, ID3D12Resource* resource_after,
			D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
		{
			auto& barrier{ _barriers[_offset] };
			barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING;
			barrier.Flags = flags;
			barrier.Aliasing.pResourceBefore = resource_before;
			barrier.Aliasing.pResourceAfter = resource_after;

			++_offset;
		}

		void apply(ID3D12GraphicsCommandList6* list)
		{
			list->ResourceBarrier(_offset, _barriers);
			_offset = 0u;
		}
	private:
		D3D12_RESOURCE_BARRIER _barriers[max_barrier_count]{};
		u32 _offset{};
	};

	struct root_parameter : public D3D12_ROOT_PARAMETER
	{
		root_parameter() = default;

		constexpr void as_descriptor_table(
			u32 numDescriptorRanges,
			D3D12_DESCRIPTOR_RANGE* pDescriptorRanges,
			D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
		{
			this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
			this->ShaderVisibility = visibility;
			this->DescriptorTable.NumDescriptorRanges = numDescriptorRanges;
			this->DescriptorTable.pDescriptorRanges = pDescriptorRanges;
		}

		/*void as_descriptor_table(
			utl::vector<D3D12_DESCRIPTOR_RANGE>& descriptor_range_array,
			D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
		{
			this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
			this->ShaderVisibility = visibility;
			this->DescriptorTable.NumDescriptorRanges = static_cast<u32>(descriptor_range_array.size());
			this->DescriptorTable.pDescriptorRanges = descriptor_range_array.data();
		}*/

		constexpr void as_constants(
			u32 num32BitValues,
			u32 shaderRegister,
			u32 registerSpace = 0u,
			D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
		{
			this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
			this->ShaderVisibility = visibility;
			this->Constants.Num32BitValues = num32BitValues;
			this->Constants.ShaderRegister = shaderRegister;
			this->Constants.RegisterSpace = registerSpace;
		}

		constexpr void as_constant_buffer_view(
			u32 shaderRegister,
			u32 registerSpace = 0u,
			D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
		{
			this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
			this->ShaderVisibility = visibility;
			this->Descriptor.ShaderRegister = shaderRegister;
			this->Descriptor.RegisterSpace = registerSpace;
		}

		constexpr void as_shader_resource_view(
			u32 shaderRegister,
			u32 registerSpace = 0u,
			D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
		{
			this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
			this->ShaderVisibility = visibility;
			this->Descriptor.ShaderRegister = shaderRegister;
			this->Descriptor.RegisterSpace = registerSpace;
		}

		constexpr void as_unordered_access_view(
			u32 shaderRegister,
			u32 registerSpace = 0u,
			D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) noexcept
		{
			this->ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
			this->ShaderVisibility = visibility;
			this->Descriptor.ShaderRegister = shaderRegister;
			this->Descriptor.RegisterSpace = registerSpace;
		}
	};

	struct resource_desc : D3D12_RESOURCE_DESC
	{
		resource_desc* as_buffer(u64 size)
		{
			Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
			Alignment = 0;
			Width = size;
			Height = 1;
			DepthOrArraySize = 1;
			MipLevels = 1;
			Format = DXGI_FORMAT_UNKNOWN;
			SampleDesc.Count = 1u;
			SampleDesc.Quality = 0u;
			Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
			Flags = D3D12_RESOURCE_FLAG_NONE;
			return this;
		}

		resource_desc* as_texture_buffer(u64 width, u32 height)
		{
			Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
			Alignment = 0;
			auto uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
			Width = height * uploadPitch;
			Height = 1;
			DepthOrArraySize = 1;
			MipLevels = 1;
			Format = DXGI_FORMAT_UNKNOWN;
			SampleDesc.Count = 1u;
			SampleDesc.Quality = 0u;
			Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
			Flags = D3D12_RESOURCE_FLAG_NONE;
			return this;
		}

		void as_texture_1d(u64 size)
		{
			Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
			Alignment = 0;
			Width = size;
			Height = 1;
			DepthOrArraySize = 1;
			MipLevels = 1;
			Format = DXGI_FORMAT_UNKNOWN;
			SampleDesc.Count = 1u;
			SampleDesc.Quality = 0u;
			Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
			Flags = D3D12_RESOURCE_FLAG_NONE;
		}

		resource_desc* as_texture_2d(u64 width, u32 height, DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE)
		{
			Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
			Alignment = 0;
			Width = width;
			Height = height;
			DepthOrArraySize = 1;
			MipLevels = 1;
			Format = format;
			SampleDesc.Count = 1u;
			SampleDesc.Quality = 0u;
			Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
			Flags = flags;
			return this;
		}

		void as_texture_3d(u64 width, u32 height, u16 depth)
		{
			Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
			Alignment = 0;
			Width = width;
			Height = height;
			DepthOrArraySize = depth;
			MipLevels = 1;
			Format = DXGI_FORMAT_UNKNOWN;
			SampleDesc.Count = 1u;
			SampleDesc.Quality = 0u;
			Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
			Flags = D3D12_RESOURCE_FLAG_NONE;
		}
	};

	struct texture_copy_location :D3D12_TEXTURE_COPY_LOCATION
	{
		texture_copy_location* as_buffer(ID3D12Resource* res, u32 width, u32 height, DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM, u32 depth = 1u)
		{
			Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
			PlacedFootprint.Footprint.Width = width;
			PlacedFootprint.Footprint.Height = height;
			PlacedFootprint.Footprint.Format = format;
			PlacedFootprint.Footprint.Depth = depth;
			PlacedFootprint.Footprint.RowPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
			PlacedFootprint.Offset = 0ull;
			pResource = res;
			return this;
		}

		texture_copy_location* as_texture(ID3D12Resource* res, u32 index)
		{
			Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
			SubresourceIndex = index;
			pResource = res;
			return this;
		}
	};

	struct view_desc
	{
		union
		{
			D3D12_CONSTANT_BUFFER_VIEW_DESC _cbv;
			D3D12_SHADER_RESOURCE_VIEW_DESC _srv;
			D3D12_UNORDERED_ACCESS_VIEW_DESC _uav;
			D3D12_RENDER_TARGET_VIEW_DESC _rtv;
			D3D12_DEPTH_STENCIL_VIEW_DESC _dsv;
		};

		constexpr D3D12_RENDER_TARGET_VIEW_DESC* as_rtv(DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM)
		{
			_rtv.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
			_rtv.Format = format;
			_rtv.Texture2D.MipSlice;
			return &_rtv;
		}

		constexpr D3D12_DEPTH_STENCIL_VIEW_DESC* as_dsv_texture_2d
		(
			DXGI_FORMAT format = DXGI_FORMAT_D32_FLOAT,
			D3D12_DSV_FLAGS flags = D3D12_DSV_FLAG_NONE
		)
		{
			_dsv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
			_dsv.Format = format;
			_dsv.Flags = flags;
			return &_dsv;
		}

		constexpr D3D12_SHADER_RESOURCE_VIEW_DESC* as_srv_texture_2d
		(
			DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM,
			UINT shader4componentmapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
			UINT miplevels = 1u
		)
		{
			_srv.Format = format;
			_srv.Shader4ComponentMapping = shader4componentmapping;
			_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
			_srv.Texture2D.MipLevels = miplevels;
			return &_srv;
		}

		constexpr D3D12_CONSTANT_BUFFER_VIEW_DESC* as_constant
		(
			D3D12_GPU_VIRTUAL_ADDRESS address,
			u32 size
		)
		{
			_cbv.BufferLocation = address;
			_cbv.SizeInBytes = size;
			return &_cbv;
		}
	};

	template <typename InnerStructType, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type, typename DefaultArg = InnerStructType>
	class alignas(void*) pipeline_state_stream_subobject
	{
	private:
		D3D12_PIPELINE_STATE_SUBOBJECT_TYPE pssType;
		InnerStructType pssInner;
	public:
		pipeline_state_stream_subobject() noexcept : pssType(Type), pssInner(DefaultArg()) {}
		pipeline_state_stream_subobject(InnerStructType const& i) noexcept : pssType(Type), pssInner(i) {}
		void operator=(InnerStructType const& i) noexcept { pssType = Type; pssInner = i; }
		void as_self() { pssType = Type; }
		operator InnerStructType const& () const noexcept { return pssInner; }
		operator InnerStructType& () noexcept { return pssInner; }
		InnerStructType* operator&() noexcept { return &pssInner; }
		InnerStructType const* operator&() const noexcept { return &pssInner; }
	};

	using pipeline_state_stream_flags = pipeline_state_stream_subobject< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS >;
	using pipeline_state_stream_node_mask = pipeline_state_stream_subobject< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK >;
	using pipeline_state_stream_root_signature = pipeline_state_stream_subobject< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE >;
	using pipeline_state_stream_input_layout = pipeline_state_stream_subobject< D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT >;
	using pipeline_state_stream_ib_strip_cut_value = pipeline_state_stream_subobject< D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE >;
	using pipeline_state_stream_primitive_topology = pipeline_state_stream_subobject< D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY >;
	using pipeline_state_stream_vs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS >;
	using pipeline_state_stream_gs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS >;
	using pipeline_state_stream_stream_output = pipeline_state_stream_subobject< D3D12_STREAM_OUTPUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT >;
	using pipeline_state_stream_hs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS >;
	using pipeline_state_stream_ds = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS >;
	using pipeline_state_stream_ps = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS >;
	using pipeline_state_stream_as = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS >;
	using pipeline_state_stream_ms = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS >;
	using pipeline_state_stream_cs = pipeline_state_stream_subobject< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS >;
	using pipeline_state_stream_blend_desc = pipeline_state_stream_subobject< D3D12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND >;
	using pipeline_state_stream_depth_stencil = pipeline_state_stream_subobject< D3D12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL >;
	using pipeline_state_stream_depth_stencil1 = pipeline_state_stream_subobject< D3D12_DEPTH_STENCIL_DESC1, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1 >;
	using pipeline_state_stream_depth_stencil_format = pipeline_state_stream_subobject< DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT >;
	using pipeline_state_stream_rasterizer = pipeline_state_stream_subobject< D3D12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER >;
	using pipeline_state_stream_render_target_formats = pipeline_state_stream_subobject< D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS >;
	using pipeline_state_stream_sample_desc = pipeline_state_stream_subobject< DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC >;
	using pipeline_state_stream_sample_mask = pipeline_state_stream_subobject< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK >;
	using pipeline_state_stream_cached_pso = pipeline_state_stream_subobject< D3D12_CACHED_PIPELINE_STATE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO >;
	using pipeline_state_stream_view_instancing = pipeline_state_stream_subobject< D3D12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING >;


	constexpr u64 stream_array[] =
	{
		sizeof(pipeline_state_stream_flags),
		sizeof(pipeline_state_stream_node_mask),
		sizeof(pipeline_state_stream_root_signature),
		sizeof(pipeline_state_stream_input_layout),
		sizeof(pipeline_state_stream_ib_strip_cut_value),
		sizeof(pipeline_state_stream_primitive_topology),
		sizeof(pipeline_state_stream_vs),
		sizeof(pipeline_state_stream_gs),
		sizeof(pipeline_state_stream_stream_output),
		sizeof(pipeline_state_stream_hs),
		sizeof(pipeline_state_stream_ds),
		sizeof(pipeline_state_stream_ps),
		sizeof(pipeline_state_stream_as),
		sizeof(pipeline_state_stream_ms),
		sizeof(pipeline_state_stream_cs),
		sizeof(pipeline_state_stream_blend_desc),
		sizeof(pipeline_state_stream_depth_stencil),
		sizeof(pipeline_state_stream_depth_stencil1),
		sizeof(pipeline_state_stream_depth_stencil_format),
		sizeof(pipeline_state_stream_rasterizer),
		sizeof(pipeline_state_stream_render_target_formats),
		sizeof(pipeline_state_stream_sample_desc),
		sizeof(pipeline_state_stream_sample_mask),
		sizeof(pipeline_state_stream_cached_pso),
		sizeof(pipeline_state_stream_view_instancing),
	};


	constexpr u64 pipeline_state_desc_size
	{
		[]()->u64
		{
		   u64 sub{};
		   for (size_t i = 0; i < _countof(stream_array); i++)
		   {
			   sub += stream_array[i];
		   }
		   return sub;
		}()
	};

	struct pipeline_state_stream_desc : D3D12_PIPELINE_STATE_STREAM_DESC
	{
		pipeline_state_stream_desc()
		{
			this->pPipelineStateSubobjectStream = malloc(pipeline_state_desc_size);
			this->SizeInBytes = 0;
		}
		~pipeline_state_stream_desc()
		{
			free(this->pPipelineStateSubobjectStream);
		}

		template<typename T>
		void add(const T& sub)
		{
			T* current_data = reinterpret_cast<T*>(reinterpret_cast<u64>(this->pPipelineStateSubobjectStream) + this->SizeInBytes);
			*current_data = sub;
			this->SizeInBytes += sizeof(T);
		}

		template<typename T>
		T& add()
		{
			T* current_data = reinterpret_cast<T*>(reinterpret_cast<u64>(this->pPipelineStateSubobjectStream) + this->SizeInBytes);
			this->SizeInBytes += sizeof(T);
			current_data->as_self();
			return *current_data;
		}
	};
}
